//! X事件处理逻辑的完整实现
const std = @import("std");
const util = @import("util");
const WinQueue = @import("list");
const Position = util.Position;
const Size = util.Size;
const Vector2D = util.Vector2D;
const WmErr = util.WmErr;
const asText = util.asText;
const testing = std.testing;
const logger = std.log;
const xcb = util.xcb;

pub const WmRuntime = struct {
    vendor: [128]u8,
    vendor_len: usize,
    protocol_major_version: u16,
    protocol_minor_version: u16,
    major: u32,
    minor: u32,
    patch: u32,
    conn: ?*xcb.xcb_connection_t,
    err: ?*xcb.xcb_errors_context_t,
    root_screen: ?*xcb.xcb_screen_t,
    clients: WinQueue,
    screen_nbr: c_int,
    wm_protocols: xcb.xcb_atom_t,
    wm_delete_window: xcb.xcb_atom_t,
    drag_start_pos: Position,
    drag_start_frame_pos: Position,
    drag_start_frame_size: Size,
    err_buf: [1024]u8,

    const Self = @This();

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    ///初始化各个变量，并建立连接
    pub fn init(display_name: ?[*:0]const u8) WmErr!*Self {
        var wm = allocator.create(WmRuntime) catch |e| {
            logger.err("allocated WmRuntime failed: {any}", .{e});
            return error.AllocatedMemoryFail;
        };
        wm.conn = xcb.xcb_connect(display_name, &wm.screen_nbr);
        if (wm.conn) |c| {
            if (xcb.xcb_connection_has_error(c) != 0) {
                return error.NotConnect;
            }
        } else {
            return error.NotConnect;
        }

        wm.clients = WinQueue.init();

        if (xcb.xcb_errors_context_new(wm.conn, &wm.err) == -1) {
            xcb.xcb_disconnect(wm.conn);
            wm.clients.deinit();
            allocator.destroy(wm);
            return error.CannotCreateErrContext;
        }

        return wm;
    }

    ///关闭连接并销毁已申请内存
    fn destroy(self: *Self) void {
        xcb.xcb_errors_context_free(self.err);
        xcb.xcb_disconnect(self.conn);
        self.clients.deinit();
        allocator.destroy(self);
        // defer 用于执行general_purpose_allocator善后工作
        defer {
            // 尝试进行 deinit 操作
            const deinit_status = gpa.deinit();

            // 检测是否发生内存泄漏
            if (deinit_status == .leak) @panic("Memory leaks");
        }
        logger.warn("Wm disconnect from server\n", .{});
    }
    ///获取失败错误信息
    fn toXcbErrorString(self: *Self, err: *xcb.xcb_generic_error_t) anyerror![]u8 {
        var extension: [*c]const u8 = null;

        defer std.c.free(err);
        const major = xcb.xcb_errors_get_name_for_major_code(
            self.err,
            err.major_code,
        );
        var minor = xcb.xcb_errors_get_name_for_minor_code(
            self.err,
            err.major_code,
            err.minor_code,
        );
        if (minor == null) {
            minor = "None";
        }

        const name = xcb.xcb_errors_get_name_for_error(
            self.err,
            err.error_code,
            &extension,
        );
        if (extension == null) {
            extension = "None";
        }

        return try std.fmt.bufPrint(&self.err_buf, "(major={s}, minor={s}), code=Bad{s}, extension={s}, sequence={d}", .{
            major,
            minor,
            name,
            extension,
            err.sequence,
        });
    }
    ///根据字符串创建X11的atom信息
    fn getInternAtom(self: *Self, name: []const u8, only_if_exists: bool) WmErr!xcb.xcb_atom_t {
        var atom: xcb.xcb_atom_t = 0;
        var cookie = std.mem.zeroes(xcb.xcb_intern_atom_cookie_t);
        var errorp: ?*xcb.xcb_generic_error_t = null;

        cookie = xcb.xcb_intern_atom(
            self.conn,
            @intFromBool(only_if_exists),
            @truncate(name.len),
            name.ptr,
        );
        const reply: *xcb.xcb_intern_atom_reply_t = xcb.xcb_intern_atom_reply(
            self.conn,
            cookie,
            &errorp,
        );
        defer std.c.free(reply);
        if (errorp) |err| {
            logger.err("Get WM_PROTOCOLS failed: {s}", .{
                toXcbErrorString(self, err) catch {
                    return error.CannotGetXcbError;
                },
            });
        } else {
            atom = reply.atom;
            if (atom == xcb.XCB_ATOM_NONE) {
                return error.NoSuchAtom;
            }
            return atom;
        }
        return error.Unkown;
    }

    ///获取显示信息
    fn getInfoOfDisplay(self: *Self) WmErr!void {
        var iter = std.mem.zeroes(xcb.xcb_screen_iterator_t);
        var screen_count: c_int = 0;
        const sp: *const xcb.xcb_setup_t = xcb.xcb_get_setup(self.conn);
        var is_found = false;

        var release = sp.release_number;
        self.major = @divTrunc(release, 10000000);
        release = @rem(release, 10000000);
        self.minor = @divTrunc(release, 100000);
        release = @rem(release, 100000);
        self.patch = @divTrunc(release, 1000);
        self.protocol_major_version = sp.protocol_major_version;
        self.protocol_minor_version = sp.protocol_minor_version;
        self.vendor_len = @intCast(xcb.xcb_setup_vendor_length(sp));
        const v = xcb.xcb_setup_vendor(sp);

        std.mem.copyForwards(u8, &self.vendor, v[0..self.vendor_len]);

        logger.debug("{s} {d}.{d}.{d} (protocol: {d}.{d})", .{
            self.vendor[0..self.vendor_len],
            self.major,
            self.minor,
            self.patch,
            self.protocol_major_version,
            self.protocol_minor_version,
        });
        screen_count = xcb.xcb_setup_roots_length(sp);
        logger.debug("Now have {} screens", .{
            screen_count,
        });

        iter = xcb.xcb_setup_roots_iterator(sp);
        while (iter.rem != 0) {
            if (self.screen_nbr == 0) {
                self.wm_protocols = getInternAtom(
                    self,
                    util.WM_PROTOCOLS,
                    false,
                ) catch {
                    return error.CannotGetWmProtocols;
                };

                self.wm_delete_window = getInternAtom(
                    self,
                    util.WM_DELETE_WINDOW,
                    false,
                ) catch {
                    return error.CannotGetWmDeleteWindow;
                };

                self.root_screen = iter.data;

                is_found = true;
            }
            _ = --self.screen_nbr;
            xcb.xcb_screen_next(&iter);
        }
        if (!is_found) {
            return error.NoDefaultScreen;
        }
    }

    /// 根据色深获取visualid
    fn get_visualid_by_depth(self: *Self, depth: u16) xcb.xcb_visualid_t {
        var depth_iter = std.mem.zeroes(xcb.xcb_depth_iterator_t);

        depth_iter = xcb.xcb_screen_allowed_depths_iterator(self.root_screen);
        while (depth_iter.rem != 0) : (xcb.xcb_depth_next(&depth_iter)) {
            if (depth_iter.data.depth != depth)
                continue;

            const visual_iter = xcb.xcb_depth_visuals_iterator(depth_iter.data);
            if (visual_iter.rem == 0)
                continue;
            return visual_iter.data.visual_id;
        }

        return 0;
    }
    ///为窗口创建边框
    fn frame(self: *Self, child: xcb.xcb_window_t, is_before: bool) WmErr!void {
        var err: ?*xcb.xcb_generic_error_t = null;
        var cookie = std.mem.zeroes(xcb.xcb_void_cookie_t);

        // 边框已存在无需添加
        if (self.clients.contains(child)) {
            return error.AlreadyFramed;
        }

        // 1. 获取窗口的属性
        const attr_cookie = xcb.xcb_get_window_attributes(
            self.conn,
            child,
        );
        const attrs: ?*xcb.xcb_get_window_attributes_reply_t = xcb.xcb_get_window_attributes_reply(
            self.conn,
            attr_cookie,
            &err,
        );

        if (attrs == null) {
            logger.err("Create frame on get attributes failed: {s}", .{
                toXcbErrorString(self, err.?) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotGetWindowAttributes;
        }
        defer std.c.free(attrs);

        const gemt_cookie = xcb.xcb_get_geometry(
            self.conn,
            child,
        );
        const gemt: ?*xcb.xcb_get_geometry_reply_t = xcb.xcb_get_geometry_reply(
            self.conn,
            gemt_cookie,
            &err,
        );

        if (gemt == null) {
            logger.err("Create frame on get geometry failed: {s}", .{
                toXcbErrorString(self, err.?) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotGetGeometry;
        }
        defer std.c.free(gemt);

        // 2. 如果窗口在窗口管理启动之前被创建如果它是可见状态或者是没有设置override_redirect标志才会为其创建边框
        if (is_before) {
            if ((attrs.?.map_state != xcb.XCB_MAP_STATE_VIEWABLE) or
                (attrs.?.override_redirect != 0))
            {
                return;
            }
        }

        // 3. 创建窗口帧.
        const parent = xcb.xcb_generate_id(self.conn);
        var mask: u32 = xcb.XCB_CW_BORDER_PIXEL | xcb.XCB_CW_BACK_PIXEL;
        const value_list = xcb.xcb_create_window_value_list_t{
            .border_pixel = util.BORDER_COLOR_UNSELECTED,
            .background_pixel = util.BG_COLOR,
        };
        cookie = xcb.xcb_create_window_aux_checked(
            self.conn,
            xcb.XCB_COPY_FROM_PARENT,
            parent,
            self.root_screen.?.root,
            gemt.?.x,
            gemt.?.y,
            gemt.?.width,
            gemt.?.height + util.TITLE_HEIGHT,
            util.BORDER_WIDTH,
            xcb.XCB_WINDOW_CLASS_INPUT_OUTPUT,
            self.root_screen.?.root_visual,
            mask,
            &value_list,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not create frame:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });

            return error.CantnotCreateFrame;
        }

        // 4. 在窗口帧上设置事件重定向.
        const attr_list = xcb.xcb_change_window_attributes_value_list_t{
            // .event_mask = xcb.XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | xcb.XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
            .event_mask = xcb.XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
                xcb.XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
            // xcb.XCB_EVENT_MASK_ENTER_WINDOW |
            // xcb.XCB_EVENT_MASK_LEAVE_WINDOW |
            // xcb.XCB_EVENT_MASK_STRUCTURE_NOTIFY |
            // xcb.XCB_EVENT_MASK_PROPERTY_CHANGE |
            // xcb.XCB_EVENT_MASK_BUTTON_PRESS |
            // xcb.XCB_EVENT_MASK_BUTTON_RELEASE |
            // xcb.XCB_EVENT_MASK_FOCUS_CHANGE,
        };
        cookie = xcb.xcb_change_window_attributes_aux_checked(
            self.conn,
            parent,
            xcb.XCB_CW_EVENT_MASK,
            &attr_list,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not create frame on change attributes:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotSubstructureRedirectFrame;
        }

        // 5. 将客户端添加到保存集中，以便在我们崩溃时能够恢复并保持活动状态
        cookie = xcb.xcb_change_save_set_checked(self.conn, xcb.XCB_SET_MODE_INSERT, child);
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not create frame on save change: {s} ", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotSaveClientSet;
        }

        // 6. 将窗口重父化作为窗口帧的字节点
        cookie = xcb.xcb_reparent_window_checked(
            self.conn,
            child,
            parent,
            0,
            util.TITLE_HEIGHT,
        ); // Offset of client window within frame.
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not create frame on reparent: {s}] ", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotReparentFrameWindow;
        }

        // 7. 使窗口帧可见.
        cookie = xcb.xcb_map_window_checked(self.conn, parent);
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not create frame on map: {s} ", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotMapFrame;
        }

        // 8. 保存窗口帧句柄.
        _ = self.clients.append(child, parent) catch |e| {
            logger.err("Set frame and window pairs failed: {}", .{e});
            return error.CannotPairedFrame;
        };

        // 9. 在客户端窗口上抓取通用窗口管理操作。
        //   a. 将窗口移动绑定快捷键为 “alt + 鼠标左键” .
        mask = xcb.XCB_EVENT_MASK_BUTTON_PRESS | xcb.XCB_EVENT_MASK_BUTTON_RELEASE | xcb.XCB_EVENT_MASK_BUTTON_MOTION;
        cookie = xcb.xcb_grab_button_checked(
            self.conn,
            @intFromBool(false),
            child,
            @intCast(mask),
            xcb.XCB_GRAB_MODE_ASYNC,
            xcb.XCB_GRAB_MODE_ASYNC,
            xcb.XCB_NONE,
            xcb.XCB_NONE,
            xcb.XCB_BUTTON_INDEX_1, // 左键
            xcb.XCB_MOD_MASK_1,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Bind alt + left button failed:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotGrabButton;
        }

        //   b. 将改变窗口大小绑定快捷键为 “alt + 鼠标右键” .
        cookie = xcb.xcb_grab_button_checked(
            self.conn,
            @intFromBool(false),
            child,
            @intCast(mask),
            xcb.XCB_GRAB_MODE_ASYNC,
            xcb.XCB_GRAB_MODE_ASYNC,
            xcb.XCB_NONE,
            xcb.XCB_NONE,
            xcb.XCB_BUTTON_INDEX_3, // 右键
            xcb.XCB_MOD_MASK_1,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Bind alt + right button failed:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotGrabButton;
        }

        //   c. 将关闭窗口快捷键绑定为 “ctrl + f4”.
        const f4 = try keysym2Keycode(self, xcb.XK_F4);
        cookie = xcb.xcb_grab_key_checked(
            self.conn,
            @intFromBool(false),
            child,
            xcb.XCB_MOD_MASK_CONTROL,
            f4,
            xcb.XCB_GRAB_MODE_ASYNC,
            xcb.XCB_GRAB_MODE_ASYNC,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Bind CTRL+F4 failed:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotGrabKey;
        }

        //   d. 切换窗口快捷键为“alt + tab” .
        const tab = try keysym2Keycode(self, xcb.XK_Tab);
        cookie = xcb.xcb_grab_key_checked(
            self.conn,
            @intFromBool(false),
            child,
            xcb.XCB_MOD_MASK_1,
            tab,
            xcb.XCB_GRAB_MODE_ASYNC,
            xcb.XCB_GRAB_MODE_ASYNC,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Bind ALT+Tab failed:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotGrabKey;
        }

        // 10. 同步配置
        xcb.xcb_aux_sync(self.conn);
        logger.debug("Create window:{}'s frame:{} success", .{ child, parent });
    }
    ///将Keysym转换为Kecode
    fn keysym2Keycode(self: *Self, syms: xcb.xcb_keysym_t) WmErr!xcb.xcb_keycode_t {
        const kmap = xcb.xcb_key_symbols_alloc(self.conn);
        if (kmap) |kp| {
            defer xcb.xcb_key_symbols_free(kp);
            const v = xcb.xcb_key_symbols_get_keycode(kp, syms)[0];
            return v;
        }

        return error.CannotGetKeycodeBySym;
    }

    ///判断窗口的protocol是否存在或设置
    fn isExistProtocol(self: *Self, w: xcb.xcb_window_t, protocol: xcb.xcb_atom_t) bool {
        var reply = std.mem.zeroes(
            xcb.xcb_icccm_get_wm_protocols_reply_t,
        );
        const cookie = xcb.xcb_icccm_get_wm_protocols(
            self.conn,
            w,
            self.wm_protocols,
        );
        const ret = xcb.xcb_icccm_get_wm_protocols_reply(
            self.conn,
            cookie,
            &reply,
            null,
        );
        if (ret == 0) {
            logger.warn("Get protocols window[{}] failed", .{w});
            return false;
        }
        defer xcb.xcb_icccm_get_wm_protocols_reply_wipe(&reply);

        for (0..reply.atoms_len) |i| {
            if (reply.atoms[i] == protocol) {
                return true;
            }
        }

        return false;
    }

    ///销毁窗口的边框
    fn unFrame(self: *Self, w: xcb.xcb_window_t) WmErr!void {
        var cookie = std.mem.zeroes(xcb.xcb_void_cookie_t);
        var err: ?*xcb.xcb_generic_error_t = null;
        if (!self.clients.contains(w)) unreachable;

        // We reverse the steps taken in Frame().
        const frm = self.clients.get(w) orelse unreachable;

        // 1. Unmap frame.
        cookie = xcb.xcb_unmap_window_checked(self.conn, frm);
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not unmap the frame: {s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotUnmapFrame;
        }

        // 2. Reparent client window.
        cookie = xcb.xcb_reparent_window_checked(
            self.conn,
            w,
            self.root_screen.?.root,
            0,
            0,
        ); // Offset of client window within root.
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not reparent[{}] the window[{}]: {s} ", .{
                self.root_screen.?.root, w,
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            // return error.CannotReparentRootWindow;
        }

        // 3. Remove client window from save set, as it is now unrelated to us.
        cookie = xcb.xcb_change_save_set_checked(self.conn, xcb.XCB_SET_MODE_DELETE, w);
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not delete window set: {s} ", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            // return error.CannotDeleteClientSet;
        }

        // 4. Destroy frame.
        cookie = xcb.xcb_destroy_window_checked(self.conn, frm);
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not destroy frame: {s} ", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotDestroyFrame;
        }

        // 5. Drop reference to frame handle.
        self.clients.remove(w);

        logger.debug("Unframed window {} [{}]", .{ w, frm });
    }

    fn onUnmapNotiry(self: *Self, ev: *xcb.xcb_unmap_notify_event_t) void {
        // 如果窗口是我们管理的客户端窗口,在收到UnmapNotify事件时销毁窗口帧。
        // 我们需要进行检查，因为我们会收到一个UnmapNotify事件，该事件是针对我们刚刚自己销毁的窗口帧
        if (!self.clients.contains(ev.window)) {
            return;
        }

        // 如果事件是由于重新父化一个在窗口管理器启动之前已映射的窗口而触发，则忽略该事件。
        //
        // 由于我们从SubstructureNotify掩码接收到UnmapNotify事件，该事件属性指定了被解除映射窗口的父窗口。
        // 这意味着来自普通客户端窗口的UnmapNotify事件应该将该属性设置为我们维护的窗口帧。
        // 只有通过重新父化现有窗口触发的UnmapNotify事件才会将该属性设置为根窗口。
        if (ev.event == self.root_screen.?.root) {
            logger.warn(
                "Ignore UnmapNotify for reparented pre-existing window {},root {}",
                .{ ev.window, self.root_screen.?.root },
            );
            return;
        }

        unFrame(self, ev.window) catch {};
    }

    fn onMapRequest(self: *Self, ev: *xcb.xcb_map_request_event_t) WmErr!void {

        // 1. Frame or re-frame window.
        try frame(self, ev.window, false);
        // 2. Actually map window.
        const cookie = xcb.xcb_map_window_checked(self.conn, ev.window);
        const err: ?*xcb.xcb_generic_error_t = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not map the window[{}]: {s}", .{
                ev.window, toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotMapWindow;
        }
    }

    fn onConfigureRequest(self: *Self, ev: *xcb.xcb_configure_request_event_t) WmErr!void {
        var err: ?*xcb.xcb_generic_error_t = null;
        var value_list = xcb.xcb_params_configure_window_t{
            .x = ev.x,
            .y = ev.y,
            .width = ev.width,
            .height = ev.height,
            .border_width = ev.border_width,
            .sibling = ev.sibling,
            .stack_mode = ev.stack_mode,
        };
        var cookie = std.mem.zeroes(xcb.xcb_void_cookie_t);

        if (self.clients.get(ev.window)) |frm| {
            value_list.height += util.TITLE_HEIGHT;
            value_list.y += util.TITLE_HEIGHT;
            cookie = xcb.xcb_aux_configure_window(
                self.conn,
                frm,
                ev.value_mask,
                &value_list,
            );
            err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not configure the frame[{}]: {s}", .{
                    frm, toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });
                // return error.CannotMapWindow; ignore error
            }
            logger.debug("ReConfigure frame [{}] to width:{},height:{}", .{
                frm,
                ev.width,
                ev.height,
            });
            value_list.height -= util.TITLE_HEIGHT;
            value_list.y -= util.TITLE_HEIGHT;
        }
        cookie = xcb.xcb_aux_configure_window(
            self.conn,
            ev.window,
            ev.value_mask,
            &value_list,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not configure the window[{}]: {s}", .{
                ev.window, toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotConfigureWindow;
        }
        logger.debug("ReConfigure window [{}] to width:{},height:{}", .{
            ev.window,
            ev.width,
            ev.height,
        });
    }

    /// 接收按钮按下事件的是窗口帧
    fn onButtonPress(self: *Self, ev: *xcb.xcb_button_press_event_t) WmErr!void {
        if (self.clients.get(ev.event)) |frm| {
            var err: ?*xcb.xcb_generic_error_t = null;
            // 1. Save initial cursor position.
            self.drag_start_pos = Position{
                .x = ev.root_x,
                .y = ev.root_y,
            };

            // 2. Save initial window info.
            const gemt_cookie = xcb.xcb_get_geometry(self.conn, frm);
            const gemt = xcb.xcb_get_geometry_reply(
                self.conn,
                gemt_cookie,
                &err,
            );
            if (gemt == null) {
                logger.err("Get geometry on button press failed: {s}", .{
                    toXcbErrorString(self, err.?) catch {
                        return error.CannotGetXcbError;
                    },
                });
                return error.CannotGetGeometry;
            }
            defer std.c.free(gemt);

            self.drag_start_frame_pos = Position{ .x = gemt.*.x, .y = gemt.*.y };
            self.drag_start_frame_size = Size{
                .width = gemt.*.width,
                .height = gemt.*.height,
            };

            // 3. Raise clicked window to top.
            var value = xcb.xcb_params_configure_window_t{
                .stack_mode = xcb.XCB_STACK_MODE_ABOVE,
            };
            const cookie = xcb.xcb_aux_configure_window(
                self.conn,
                frm,
                xcb.XCB_CONFIG_WINDOW_STACK_MODE,
                &value,
            );
            err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not raise the frame[{}]: {s}", .{
                    frm, toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });
                return error.CannotRaiseWindow;
            }
        } else {
            logger.err("Button pressed not found the [{}]'s frame", .{ev.event});
            return error.NotFoundFrameFromMap;
        }
    }

    fn onButtonRelease(_: *Self, _: *xcb.xcb_button_release_event_t) void {}

    /// 只接收窗口帧的鼠标移动事件
    fn onMotionNotify(self: *Self, ev: *xcb.xcb_motion_notify_event_t) WmErr!void {
        var frm: xcb.xcb_window_t = 0;
        var err: ?*xcb.xcb_generic_error_t = null;
        var cookie = std.mem.zeroes(xcb.xcb_void_cookie_t);

        if (!self.clients.contains(ev.event)) {
            logger.err("Motion not found the [{}]'s frame", .{ev.event});
            return error.NotFoundFrameFromMap;
        }
        frm = self.clients.get(ev.event) orelse unreachable;
        const drag_pos = Position{
            .x = ev.root_x,
            .y = ev.root_y,
        };
        const delta = Vector2D{
            .x = drag_pos.x - self.drag_start_pos.x,
            .y = drag_pos.y - self.drag_start_pos.y,
        };

        if ((ev.state & xcb.XCB_BUTTON_MASK_1) != 0) {

            // alt + left button: Move window.
            const dest_frame_pos = Position{
                .x = self.drag_start_frame_pos.x + delta.x,
                .y = self.drag_start_frame_pos.y + delta.y,
            };
            const mv_var = xcb.xcb_params_configure_window_t{
                .x = dest_frame_pos.x,
                .y = dest_frame_pos.y,
            };

            cookie = xcb.xcb_aux_configure_window(
                self.conn,
                frm,
                xcb.XCB_CONFIG_WINDOW_X | xcb.XCB_CONFIG_WINDOW_Y,
                &mv_var,
            );
            err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not move the frame[{}]: {s}", .{
                    frm, toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });
                return error.CannotMoveWindow;
            }
        } else if ((ev.state & xcb.XCB_BUTTON_MASK_3) != 0) {
            // alt + right button: Resize window.
            // Window dimensions cannot be negative.
            const start_width: i32 = @intCast(self.drag_start_frame_size.width);
            const start_height: i32 = @intCast(self.drag_start_frame_size.height);
            const size_delta = Vector2D{
                //x y
                .x = if (delta.x > -start_width) delta.x else -start_width,
                .y = if (delta.y > -start_height) delta.y else -start_height,
            };
            const iwidth: u32 = @intCast(start_width + size_delta.x);
            const iheight: u32 = @intCast(start_height + size_delta.y);
            const dest_frame_size = Size{
                .width = iwidth,
                .height = iheight,
            };
            // 1. Resize frame.
            const resize_var = xcb.xcb_params_configure_window_t{
                .width = dest_frame_size.width,
                .height = dest_frame_size.height + util.TITLE_HEIGHT,
            };

            cookie = xcb.xcb_aux_configure_window(
                self.conn,
                frm,
                xcb.XCB_CONFIG_WINDOW_WIDTH | xcb.XCB_CONFIG_WINDOW_HEIGHT,
                &resize_var,
            );
            err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not resize the frame[{}]: {s}", .{
                    frm, toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });
                return error.CannotResizeWindow;
            }
            // 2. Resize client window.
            cookie = xcb.xcb_aux_configure_window(
                self.conn,
                ev.event,
                xcb.XCB_CONFIG_WINDOW_WIDTH | xcb.XCB_CONFIG_WINDOW_HEIGHT,
                &resize_var,
            );
            err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not resize the window[{}]: {s}", .{
                    ev.event, toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });
                return error.CannotResizeWindow;
            }
        }
    }

    fn onKeyPress(self: *Self, ev: *xcb.xcb_key_press_event_t) WmErr!void {
        var cookie = std.mem.zeroes(xcb.xcb_void_cookie_t);
        var err: ?*xcb.xcb_generic_error_t = null;

        if ((ev.state & xcb.XCB_MOD_MASK_CONTROL) != 0 and
            (ev.detail == try keysym2Keycode(self, xcb.XK_F4)))
        {
            // alt + f4: Close window.
            //
            // There are two ways to tell an X window to close. The first is to send it
            // a message of type WM_PROTOCOLS and value WM_DELETE_WINDOW. If the client
            // has not explicitly marked itself as supporting this more civilized
            // behavior (using XSetWMProtocols()), we kill it with XKillClient().
            if (isExistProtocol(self, ev.event, self.wm_delete_window)) {
                logger.debug("Gracefully deleting window {}", .{ev.event});
                // 1. Construct message.
                var msg = std.mem.zeroes(xcb.xcb_client_message_event_t);

                msg.response_type = xcb.XCB_CLIENT_MESSAGE;
                msg.type = self.wm_protocols;
                msg.window = ev.event;
                msg.format = 32;
                msg.data.data32[0] = self.wm_delete_window;

                // 2. Send message to window to be closed.
                cookie = xcb.xcb_send_event_checked(
                    self.conn,
                    0,
                    ev.event,
                    0,
                    @ptrCast(&msg),
                );
                err = xcb.xcb_request_check(self.conn, cookie);
                if (err) |e| {
                    logger.err("Could not send message to window[{}] to be closed: {s}", .{
                        ev.event, toXcbErrorString(self, e) catch {
                            return error.CannotGetXcbError;
                        },
                    });
                    return error.CannotSendCloseMsg;
                }
            } else {
                logger.debug("Killing window {}", .{ev.event});
                cookie = xcb.xcb_kill_client_checked(self.conn, ev.event);
                err = xcb.xcb_request_check(self.conn, cookie);
                if (err) |e| {
                    logger.err("Could not kill window[{}] : {s}", .{
                        ev.event, toXcbErrorString(self, e) catch {
                            return error.CannotGetXcbError;
                        },
                    });
                    return error.CannotKillWindow;
                }
            }
        } else if ((ev.state & xcb.XCB_MOD_MASK_1) != 0 and
            (ev.detail == try keysym2Keycode(self, xcb.XK_Tab)))
        {
            // alt + tab: Switch window.
            // 1. Find next window.
            var iter = self.clients.head;
            var cur: ?*WinQueue.WinList = null;
            var next_win: u32 = undefined;
            var next_frame: u32 = undefined;
            var is_find = false;

            while (iter) |node| : (iter = node.next) {
                if (node.data.key == ev.event) {
                    is_find = true;
                    cur = node.next; //指向下一个窗口
                    break;
                }
            }

            if (!is_find) {
                logger.err("Can not switch window [{}]", .{ev.event});
                return error.CannotSwitchWindow;
            }

            if (cur) |v| { //说明不是最后一个窗口
                next_win = v.data.key;
                next_frame = v.data.value;
            } else { //最后一个窗口
                cur = self.clients.head;
                next_win = cur.?.data.key;
                next_frame = cur.?.data.value;
            }

            // 2. Raise and set focus.
            var value = xcb.xcb_params_configure_window_t{
                .stack_mode = xcb.XCB_STACK_MODE_ABOVE,
            };
            cookie = xcb.xcb_aux_configure_window(
                self.conn,
                next_frame,
                xcb.XCB_CONFIG_WINDOW_STACK_MODE,
                &value,
            );
            err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not raise the frame[{}]: {s}", .{
                    next_frame, toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });
                return error.CannotRaiseWindow;
            }

            cookie = xcb.xcb_set_input_focus_checked(
                self.conn,
                xcb.XCB_INPUT_FOCUS_POINTER_ROOT,
                next_win,
                xcb.XCB_CURRENT_TIME,
            );
            err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not focus the window[{}]: {s}", .{
                    next_win, toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });
                return error.CannotRaiseWindow;
            }
        }
    }

    fn checkUtilNotMotionEvent(self: *Self, ev: *xcb.xcb_motion_notify_event_t) WmErr!bool {
        var prev: ?*xcb.xcb_motion_notify_event_t = null;
        var event: ?*xcb.xcb_generic_event_t = null;
        logger.debug("####2", .{});
        defer {
            if (prev) |p| {
                ev.* = p.*;
                std.c.free(p);
            }
        }
        while (true) {
            event = xcb.xcb_poll_for_event(self.conn);
            if (event) |e| {
                const tp = e.response_type & ~@as(u8, 0x80);
                if (tp != xcb.XCB_MOTION_NOTIFY) {
                    break;
                }
                const tmp: *xcb.xcb_motion_notify_event_t = @ptrCast(e);
                if (tmp.child == xcb.XCB_NONE) break;
                if (prev) |p| {
                    std.c.free(p);
                    prev = tmp;
                }
            } else {
                return error.PipeBroken;
            }
        }
        std.c.free(event);

        logger.debug("####1", .{});
        return true;
    }

    fn onEnterNotify(self: *Self, ev: *xcb.xcb_enter_notify_event_t) WmErr!void {
        try setFocus(self, ev.event);
    }

    fn setFocusColor(self: *Self, win: xcb.xcb_window_t, isFocus: bool) WmErr!void {
        if (util.BORDER_WIDTH > 0 and self.root_screen.?.root != win and win != 0) {
            const val = xcb.xcb_change_window_attributes_value_list_t{
                .border_pixel = if (isFocus) util.BORDER_COLOR_SELECTED else util.BORDER_COLOR_UNSELECTED,
            };

            const cookie = xcb.xcb_change_window_attributes_aux_checked(
                self.conn,
                win,
                xcb.XCB_CW_BORDER_PIXEL,
                &val,
            );

            const err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not set {} color of {s}: {s}", .{
                    win,
                    if (isFocus) "focused" else "unfocused",
                    toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });

                return error.CannotSubstructureRedirect;
            }

            if (xcb.xcb_flush(self.conn) <= 0) {
                return error.CannotFlushConn;
            }
        }
    }
    fn onFocusIn(self: *Self, ev: *xcb.xcb_focus_in_event_t) WmErr!void {
        try setFocusColor(self, ev.event, true);
    }

    fn onFocusOut(self: *Self, ev: *xcb.xcb_focus_out_event_t) WmErr!void {
        try setFocusColor(self, ev.event, false);
    }

    fn setFocus(self: *Self, w: xcb.xcb_drawable_t) WmErr!void {
        if (w != 0 and w != self.root_screen.?.root) {
            const cookie = xcb.xcb_set_input_focus_checked(
                self.conn,
                xcb.XCB_INPUT_FOCUS_POINTER_ROOT,
                w,
                xcb.XCB_CURRENT_TIME,
            );
            const err = xcb.xcb_request_check(self.conn, cookie);
            if (err) |e| {
                logger.err("Could not set {} focus: {s}", .{
                    w,
                    toXcbErrorString(self, e) catch {
                        return error.CannotGetXcbError;
                    },
                });

                return error.CannotSubstructureRedirect;
            }
        }
    }

    fn prepare(self: *Self) WmErr!void {
        var cookie = std.mem.zeroes(xcb.xcb_void_cookie_t);
        var err: ?*xcb.xcb_generic_error_t = null;
        var query_cookie = std.mem.zeroes(
            xcb.xcb_query_tree_cookie_t,
        );
        var query_reply: ?*xcb.xcb_query_tree_reply_t = null;

        errdefer {
            self.destroy();
        }

        try getInfoOfDisplay(self);

        // 1. 将根窗口设置为事件重定向
        const value_list = xcb.xcb_change_window_attributes_value_list_t{
            .event_mask = xcb.XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
        };
        cookie = xcb.xcb_change_window_attributes_aux_checked(
            self.conn,
            self.root_screen.?.root,
            xcb.XCB_CW_EVENT_MASK,
            &value_list,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not set substructrue redirect: {s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });

            return error.CannotSubstructureRedirect;
        }

        // 2. 解除所有的绑定键
        cookie = xcb.xcb_ungrab_key_checked(
            self.conn,
            xcb.XCB_GRAB_ANY,
            self.root_screen.?.root,
            xcb.XCB_MOD_MASK_ANY,
        );
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not ungrab key: {s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });

            return error.CannotSubstructureRedirect;
        }

        //  3. 抓取X服务器，以防窗口在我们为其添加边框时发生变化
        cookie = xcb.xcb_grab_server_checked(self.conn);
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not grab server:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotGrabServer;
        }
        xcb.xcb_aux_sync(self.conn);
        //  4. 为所有顶级窗口添加边框
        //   4.1 查询现存的顶级窗口
        query_cookie = xcb.xcb_query_tree(self.conn, self.root_screen.?.root);
        query_reply = xcb.xcb_query_tree_reply(self.conn, query_cookie, &err);
        if (query_reply) |reply| {
            const returned_root = reply.root;
            if (returned_root != self.root_screen.?.root) {
                std.c.free(reply);
                return error.CannotGetRootWindow;
            }
            const top_level_windows = xcb.xcb_query_tree_children(reply);

            for (0..@intCast(xcb.xcb_query_tree_children_length(reply))) |i| {
                //was_created_before_window_manager
                logger.debug("Exist window {} create frame", .{
                    top_level_windows[i],
                });
                // 4.2 为窗口加上边框
                try frame(self, top_level_windows[i], true);
            }
            //     4.3 释放内存.
            std.c.free(reply);
        } else {
            const e = err orelse return error.ErrorIsNull;
            logger.err("Could not query tree:{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotQueryWindowTree;
        }

        //  5. 解除抓取状态
        cookie = xcb.xcb_ungrab_server_checked(self.conn);
        err = xcb.xcb_request_check(self.conn, cookie);
        if (err) |e| {
            logger.err("Could not ungrab server, code{s}", .{
                toXcbErrorString(self, e) catch {
                    return error.CannotGetXcbError;
                },
            });
            return error.CannotUngrabServer;
        }
        xcb.xcb_aux_sync(self.conn);
    }
    pub fn run(self: *Self) WmErr!void {
        var event: ?*xcb.xcb_generic_event_t = null;
        var tp: u8 = undefined;

        try self.prepare();

        while (true) {
            event = xcb.xcb_wait_for_event(self.conn);
            if (event) |ev| {
                tp = ev.response_type & ~@as(u8, 0x80);
                logger.debug("{s}", .{xcb.xcb_event_get_label(tp)});
                switch (tp) {
                    xcb.XCB_UNMAP_NOTIFY => {
                        onUnmapNotiry(self, @ptrCast(ev));
                    },
                    xcb.XCB_MAP_REQUEST => {
                        onMapRequest(self, @ptrCast(ev)) catch |e| {
                            logger.warn("Map reauest error: {any}", .{e});
                        };
                    },
                    xcb.XCB_CONFIGURE_REQUEST => {
                        onConfigureRequest(self, @ptrCast(ev)) catch |e| {
                            logger.warn("Configure reauest error: {any}", .{e});
                        };
                    },
                    xcb.XCB_BUTTON_PRESS => {
                        onButtonPress(self, @ptrCast(ev)) catch |e| {
                            logger.warn("Button press error: {any}", .{e});
                        };
                    },
                    xcb.XCB_BUTTON_RELEASE => {
                        onButtonRelease(self, @ptrCast(ev));
                    },
                    xcb.XCB_MOTION_NOTIFY => {

                        // Skip any already pending motion events.
                        // _ = checkUtilNotMotionEvent(self, @ptrCast(ev)) catch {
                        //     logger.err("Recieve error: {s}", .{
                        //         "Pipe broken",
                        //     });
                        //     break;
                        // };
                        onMotionNotify(self, @ptrCast(ev)) catch |e| {
                            logger.warn("Motion notify error: {any}", .{e});
                        };
                    },
                    xcb.XCB_KEY_PRESS => {
                        onKeyPress(self, @ptrCast(ev)) catch |e| {
                            logger.warn("Key press error: {any}", .{e});
                        };
                    },
                    // xcb.XCB_ENTER_NOTIFY => {
                    //     onEnterNotify(self, @ptrCast(ev)) catch |e| {
                    //         logger.warn("Enter notify error: {any}", .{e});
                    //     };
                    // },
                    // xcb.XCB_FOCUS_IN => {
                    //     onFocusIn(self, @ptrCast(ev)) catch |e| {
                    //         logger.warn("Focus in error: {any}", .{e});
                    //     };
                    // },
                    // xcb.XCB_FOCUS_OUT => {
                    //     onFocusOut(self, @ptrCast(ev)) catch |e| {
                    //         logger.warn("Focus out error: {any}", .{e});
                    //     };
                    // },
                    else => logger.warn("unkown event {}: {s}", .{
                        tp,
                        xcb.xcb_event_get_label(tp),
                    }),
                }
                std.c.free(ev);
            } else {
                logger.err("Recieve error: {s}", .{
                    "Pipe broken",
                });
                break;
            }
        }
    }
};
